using System;

namespace Measurements
{
    public delegate double ToBaseUnitDelegate(double measurement);
    public delegate double FromBaseUnitDelegate(double measurement);
    
    public class MeasurementUnit
    {
        private MeasurementUnit _BaseUnit;
        
        public string Name { get; private set; }
        public string Unit { get; private set; }

        public MeasurementUnit BaseUnit 
        { 
            get
            {
                return _BaseUnit ?? this;   
            }
            private set
            {
                _BaseUnit = value;   
            }
        }
        
        public ToBaseUnitDelegate ToBaseUnit { get; private set; }
        public FromBaseUnitDelegate FromBaseUnit { get; private set; }
  
        public MeasurementUnit(string name, string unit, MeasurementUnit baseUnit, ToBaseUnitDelegate toBaseUnit, FromBaseUnitDelegate fromBaseUnit)
        {
            if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
            if (string.IsNullOrEmpty(unit)) throw new ArgumentNullException("unit");
            if (toBaseUnit == null) throw new ArgumentNullException("toBaseUnit");
            if (fromBaseUnit == null) throw new ArgumentNullException("fromBaseUnit");
            
            Name = name;
            Unit = unit;
            BaseUnit = baseUnit;
            ToBaseUnit = toBaseUnit;
            FromBaseUnit = fromBaseUnit;
        }
        
        public override string ToString()
        {
            return Name;
        }
    }
    
    public class MeasurementUnitConvertible<TUnit> where TUnit : MeasurementUnit
    {
        private double _Measurement;
        
        public double Measurement
        {
            get { return _Measurement; }
        }
        
        public MeasurementUnitConvertible(double measurement)
        {
            _Measurement = measurement;   
        }
        
        public MeasurementUnitConverter<TUnit> From(TUnit fromUnit)
        {
            return new MeasurementUnitConverter<TUnit>(fromUnit, Measurement);
        }
    }
    
    public class MeasurementUnitConverter<TUnit> where TUnit : MeasurementUnit
    {
        private double _Measurement;
        private TUnit _FromUnit;
        
        public TUnit FromUnit
        {
            get { return _FromUnit; }            
        }
        
        public double Measurement
        {
            get { return _Measurement; }   
        }
        
        public MeasurementUnitConverter(TUnit fromUnit, double measurement)
        {
            if (fromUnit == null) throw new ArgumentNullException("fromUnit");
            
            _FromUnit = fromUnit;
            _Measurement = measurement;
        }
        
        public double To(TUnit toUnit)
        {
            if (toUnit == null) throw new ArgumentNullException("toUnit");
            
            var fromVal = FromUnit.ToBaseUnit(Measurement);
            var toVal = toUnit.FromBaseUnit(fromVal);
            
            return toVal;
        }
    }
}
